package cn.iocoder.yudao.module.trade.service.brokerage;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.collection.ListUtil;
import cn.hutool.core.util.*;
import cn.hutool.extra.spring.SpringUtil;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.framework.common.util.number.MoneyUtils;
import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX;
import cn.iocoder.yudao.framework.mybatis.core.util.MyBatisUtils;
import cn.iocoder.yudao.module.product.api.sku.ProductSkuApi;
import cn.iocoder.yudao.module.product.api.sku.dto.ProductSkuRespDTO;
import cn.iocoder.yudao.module.product.api.spu.ProductSpuApi;
import cn.iocoder.yudao.module.product.api.spu.dto.ProductSpuRespDTO;
import cn.iocoder.yudao.module.trade.controller.admin.brokerage.vo.record.BrokerageRecordPageReqVO;
import cn.iocoder.yudao.module.trade.controller.app.brokerage.vo.record.AppBrokerageProductPriceRespVO;
import cn.iocoder.yudao.module.trade.controller.app.brokerage.vo.user.AppBrokerageUserRankByPriceRespVO;
import cn.iocoder.yudao.module.trade.controller.app.brokerage.vo.user.AppBrokerageUserRankPageReqVO;
import cn.iocoder.yudao.module.trade.convert.brokerage.BrokerageRecordConvert;
import cn.iocoder.yudao.module.trade.dal.dataobject.brokerage.BrokerageRecordDO;
import cn.iocoder.yudao.module.trade.dal.dataobject.brokerage.BrokerageUserDO;
import cn.iocoder.yudao.module.trade.dal.dataobject.config.TradeConfigDO;
import cn.iocoder.yudao.module.trade.dal.mysql.brokerage.BrokerageRecordMapper;
import cn.iocoder.yudao.module.trade.enums.brokerage.BrokerageRecordBizTypeEnum;
import cn.iocoder.yudao.module.trade.enums.brokerage.BrokerageRecordStatusEnum;
import cn.iocoder.yudao.module.trade.service.brokerage.bo.BrokerageAddReqBO;
import cn.iocoder.yudao.module.trade.service.brokerage.bo.UserBrokerageSummaryRespBO;
import cn.iocoder.yudao.module.trade.service.config.TradeConfigService;
import com.baomidou.mybatisplus.core.metadata.IPage;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.validation.annotation.Validated;

import javax.annotation.Resource;
import java.time.LocalDateTime;
import java.util.*;
import java.util.function.Function;
import java.util.stream.Collectors;

import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.getMaxValue;
import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.getMinValue;
import static cn.iocoder.yudao.framework.web.core.util.WebFrameworkUtils.getLoginUserId;
import static cn.iocoder.yudao.module.trade.enums.ErrorCodeConstants.BROKERAGE_WITHDRAW_USER_BALANCE_NOT_ENOUGH;

/**
 * 佣金记录 Service 实现类
 *
 * @author owen
 */
@Slf4j
@Service
@Validated
public class BrokerageRecordServiceImpl implements BrokerageRecordService {

    @Resource
    private BrokerageRecordMapper brokerageRecordMapper;
    @Resource
    private TradeConfigService tradeConfigService;
    @Resource
    private BrokerageUserService brokerageUserService;

    @Resource
    private ProductSpuApi productSpuApi;
    @Resource
    private ProductSkuApi productSkuApi;

    @Override
    public BrokerageRecordDO getBrokerageRecord(Long id) {
        return brokerageRecordMapper.selectById(id);
    }

    @Override
    public PageResult<BrokerageRecordDO> getBrokerageRecordPage(BrokerageRecordPageReqVO pageReqVO) {
        return brokerageRecordMapper.selectPage(pageReqVO);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void addBrokerage(Long userId, BrokerageRecordBizTypeEnum bizType, List<BrokerageAddReqBO> list) {
        // 获取交易配置，判断是否启用了分销功能
        TradeConfigDO memberConfig = tradeConfigService.getTradeConfig();
        // 0 未启用分销功能
        if (memberConfig == null || !BooleanUtil.isTrue(memberConfig.getBrokerageEnabled())) {
            log.error("[addBrokerage][增加佣金失败：brokerageEnabled 未配置，userId({}) bizType({}) list({})", userId, bizType, list);
            return;
        }
        // 1.1 获得当前下单用户的 一级推广人
        BrokerageUserDO firstUser = brokerageUserService.getBindBrokerageUser(userId);
        if (firstUser == null || !BooleanUtil.isTrue(firstUser.getBrokerageEnabled())) {
            return;
        }
        // 1.2 计算一级分佣
        addBrokerage(
                firstUser,
                list,
                memberConfig.getBrokerageFrozenDays(), // 冻结天数
                memberConfig.getBrokerageFirstPercent(), // 一级佣金比例
                bizType, 1);

        // 2.1 获得二级推广员
        if (firstUser.getBindUserId() == null) {
            return;
        }
        // 获得当前用户的分销用户信息，（这里查询的是下单用户的一级的上级用户）
        BrokerageUserDO secondUser = brokerageUserService.getBrokerageUser(firstUser.getBindUserId());
        if (secondUser == null || !BooleanUtil.isTrue(secondUser.getBrokerageEnabled())) {
            return;
        }
        // 2.2 计算二级分佣
        addBrokerage(
                secondUser,
                list,
                memberConfig.getBrokerageFrozenDays(), // 冻结天数
                memberConfig.getBrokerageSecondPercent(), // 二级佣金比例
                bizType, 2);

//        // 定义每一级的佣金比例（可以改为从配置表动态获取）
//        List<Integer> brokeragePercents = Arrays.asList(
//                memberConfig.getBrokerageFirstPercent(),
//                memberConfig.getBrokerageSecondPercent(),
//                memberConfig.getBrokerageThirdPercent(),    // 新增配置字段
//                memberConfig.getBrokerageFourthPercent(),   // 新增配置字段
//                memberConfig.getBrokerageFifthPercent(),    // 新增配置字段
//                memberConfig.getBrokerageSixthPercent(),    // 新增配置字段
//                memberConfig.getBrokerageSeventhPercent()   // 新增配置字段
//        );
//        // 当前被推广的用户ID（下单用户）
//        Long currentUserId = userId;
//        // 第一次：获取当前用户绑定的上级推广人
//        BrokerageUserDO currentBrokerageUser = brokerageUserService.getBindBrokerageUser(currentUserId);
//
//        // 遍历每一级推广关系，最多7级
//        for (int level = 1; level <= brokeragePercents.size(); level++) {
//            if (currentBrokerageUser == null || !BooleanUtil.isTrue(currentBrokerageUser.getBrokerageEnabled())) {
//                break; // 没有上级或未启用分销，结束分佣流程
//            }
//            // 获取该级的佣金比例（保护性检查）
//            if (level > brokeragePercents.size()) {
//                break;
//            }
//            // 获取该级的佣金比例
//            Integer percent = brokeragePercents.get(level - 1);
//            if (percent == null || percent <= 0) {
//                currentUserId = currentBrokerageUser.getBindUserId(); // 跳过该级，但继续查下一层
//                currentBrokerageUser = brokerageUserService.getBrokerageUser(currentUserId); // 使用 getBrokerageUser
//                continue;
//            }
//            // 添加该级推广员的佣金记录
//            addBrokerage(
//                    currentBrokerageUser,
//                    list,
//                    memberConfig.getBrokerageFrozenDays(), // 冻结天数
//                    percent,
//                    bizType,
//                    level
//            );
//
//            // 准备下一级
//            currentUserId = currentBrokerageUser.getBindUserId();
//            if (currentUserId == null) {
//                break;
//            }
//            currentBrokerageUser = brokerageUserService.getBrokerageUser(currentUserId); // 后续调用 getBrokerageUser
//        }
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void cancelBrokerage(BrokerageRecordBizTypeEnum bizType, String bizId) {
        List<BrokerageRecordDO> records = brokerageRecordMapper.selectListByBizTypeAndBizId(bizType.getType(), bizId);
        if (CollUtil.isEmpty(records)) {
            log.error("[cancelBrokerage][bizId({}) bizType({}) 更新为已失效失败：记录不存在]", bizId, bizType);
            return;
        }

        records.forEach(record -> {
            // 1. 更新佣金记录为已失效
            BrokerageRecordDO updateObj = new BrokerageRecordDO().setStatus(BrokerageRecordStatusEnum.CANCEL.getStatus());
            int updateRows = brokerageRecordMapper.updateByIdAndStatus(record.getId(), record.getStatus(), updateObj);
            if (updateRows == 0) {
                log.error("[cancelBrokerage][record({}) 更新为已失效失败]", record.getId());
                return;
            }

            // 2. 更新用户的佣金
            if (BrokerageRecordStatusEnum.WAIT_SETTLEMENT.getStatus().equals(record.getStatus())) {
                brokerageUserService.updateUserFrozenPrice(record.getUserId(), -record.getPrice());
            } else if (BrokerageRecordStatusEnum.SETTLEMENT.getStatus().equals(record.getStatus())) {
                brokerageUserService.updateUserPrice(record.getUserId(), -record.getPrice());
            }
        });
    }

    /**
     * 计算佣金金额
     *
     * @param basePrice  佣金基数（通常为订单金额、商品价格等）
     * @param percent    佣金比例（例如：10 表示 10%，不含 % 符号）
     * @param fixedPrice 固定佣金（优先使用，如果存在则忽略比例计算）
     * @return 实际应得佣金金额，单位与 basePrice 保持一致（如分）
     */
    int calculatePrice(Integer basePrice, Integer percent, Integer fixedPrice) {
        // 1. 优先使用固定佣金值（如果设置了固定佣金）
        if (fixedPrice != null && fixedPrice > 0) {
            // defaultIfNull 是为了容错（虽然这里其实 fixedPrice 已非 null，但更安全）
            return ObjectUtil.defaultIfNull(fixedPrice, 0);
        }
        // 2. 若无固定佣金，按比例计算（必须保证 basePrice 和 percent 均有效）
        if (basePrice != null && basePrice > 0 && percent != null && percent > 0) {
            // 调用工具类，计算按比例取整后的佣金金额
            // 注意：percent 为整数形式（如 10 表示 10%），不是小数
            return MoneyUtils.calculateRatePriceFloor(basePrice, Double.valueOf(percent));
        }

        // 3. 参数无效或佣金为0，返回0
        return 0;
    }

    /**
     * 增加用户佣金
     *
     * @param user                推广的用户
     * @param list                佣金增加参数列表
     * @param brokerageFrozenDays 冻结天数
     * @param brokeragePercent    佣金比例
     * @param bizType             业务类型
     * @param sourceUserLevel     来源用户等级
     */
    private void addBrokerage(BrokerageUserDO user, List<BrokerageAddReqBO> list, Integer brokerageFrozenDays,
                              Integer brokeragePercent, BrokerageRecordBizTypeEnum bizType, Integer sourceUserLevel) {
        // 1. 判断用户等级合法性
        if (sourceUserLevel < 1 || sourceUserLevel > 7) {
            throw new IllegalArgumentException(StrUtil.format("用户等级({}) 不合法", sourceUserLevel));
        }
        // 2. 构建等级 -> 固定价格提取函数的映射（Java 8 Map + Lambda）
        Map<Integer, Function<BrokerageAddReqBO, Integer>> fixedPriceGetterMap = new HashMap<>();
        fixedPriceGetterMap.put(1, BrokerageAddReqBO::getFirstFixedPrice);
        fixedPriceGetterMap.put(2, BrokerageAddReqBO::getSecondFixedPrice);
        // 3~7默认使用 null（未定义固定价格）

        // 3. 计算解冻时间
        LocalDateTime unfreezeTime = null;
        if (brokerageFrozenDays != null && brokerageFrozenDays > 0) {
            unfreezeTime = LocalDateTime.now().plusDays(brokerageFrozenDays);
        }

        // 4. 遍历佣金项
        int totalBrokerage = 0;
        List<BrokerageRecordDO> records = new ArrayList<>();
        for (BrokerageAddReqBO item : list) {
            // 4.1 获取固定金额（如果有）
            Integer fixedPrice = Optional.ofNullable(fixedPriceGetterMap.get(sourceUserLevel))
                    .map(func -> func.apply(item))
                    .orElse(null);
            // 4.2 计算佣金
            int brokeragePrice = calculatePrice(item.getBasePrice(), brokeragePercent, fixedPrice);
            if (brokeragePrice <= 0) {
                continue;
            }
            totalBrokerage += brokeragePrice;

            // 4.3 构建记录
            records.add(BrokerageRecordConvert.INSTANCE.convert(user, bizType, item.getBizId(),
                    brokerageFrozenDays, brokeragePrice, unfreezeTime, item.getTitle(),
                    item.getSourceUserId(), sourceUserLevel));
        }
        if (CollUtil.isEmpty(records)) {
            return;
        }
        // 1.3 保存佣金记录
        brokerageRecordMapper.insertBatch(records);

        // 2. 更新用户佣金(判断当前佣金是否需要冻结)
        if (brokerageFrozenDays != null && brokerageFrozenDays > 0) { // 更新用户冻结佣金
            brokerageUserService.updateUserFrozenPrice(user.getId(), totalBrokerage);
        } else { // 更新用户可用佣金
            brokerageUserService.updateUserPrice(user.getId(), totalBrokerage);
        }
    }


    @Override
    public int unfreezeRecord() {
        // 1. 查询待结算的佣金记录
        List<BrokerageRecordDO> records = brokerageRecordMapper.selectListByStatusAndUnfreezeTimeLt(
                BrokerageRecordStatusEnum.WAIT_SETTLEMENT.getStatus(), LocalDateTime.now());
        if (CollUtil.isEmpty(records)) {
            return 0;
        }

        // 2. 遍历执行
        int count = 0;
        for (BrokerageRecordDO record : records) {
            try {
                boolean success = getSelf().unfreezeRecord(record);
                if (success) {
                    count++;
                }
            } catch (Exception e) {
                log.error("[unfreezeRecord][record({}) 更新为已结算失败]", record.getId(), e);
            }
        }
        return count;
    }

    /**
     * 解冻单条佣金记录
     *
     * @param record 佣金记录
     * @return 解冻是否成功
     */
    @Transactional(rollbackFor = Exception.class)
    public boolean unfreezeRecord(BrokerageRecordDO record) {
        // 更新记录状态
        BrokerageRecordDO updateObj = new BrokerageRecordDO()
                .setStatus(BrokerageRecordStatusEnum.SETTLEMENT.getStatus())
                .setUnfreezeTime(LocalDateTime.now());
        int updateRows = brokerageRecordMapper.updateByIdAndStatus(record.getId(), record.getStatus(), updateObj);
        if (updateRows == 0) {
            log.error("[unfreezeRecord][record({}) 更新为已结算失败]", record.getId());
            return false;
        }

        // 更新用户冻结佣金
        brokerageUserService.updateFrozenPriceDecrAndPriceIncr(record.getUserId(), -record.getPrice());
        log.info("[unfreezeRecord][record({}) 更新为已结算成功]", record.getId());
        return true;
    }

    @Override
    public List<UserBrokerageSummaryRespBO> getUserBrokerageSummaryListByUserId(Collection<Long> userIds,
                                                                                Integer bizType, Integer status) {
        if (CollUtil.isEmpty(userIds)) {
            return Collections.emptyList();
        }
        return brokerageRecordMapper.selectCountAndSumPriceByUserIdInAndBizTypeAndStatus(userIds, bizType, status);
    }

    @Override
    public Integer getSummaryPriceByUserId(Long userId, BrokerageRecordBizTypeEnum bizType, BrokerageRecordStatusEnum status,
                                           LocalDateTime beginTime, LocalDateTime endTime) {
        return brokerageRecordMapper.selectSummaryPriceByUserIdAndBizTypeAndCreateTimeBetween(userId,
                bizType.getType(), status.getStatus(), beginTime, endTime);
    }

    @Override
    public PageResult<AppBrokerageUserRankByPriceRespVO> getBrokerageUserChildSummaryPageByPrice(AppBrokerageUserRankPageReqVO pageReqVO) {
        IPage<AppBrokerageUserRankByPriceRespVO> pageResult = brokerageRecordMapper.selectSummaryPricePageGroupByUserId(
                MyBatisUtils.buildPage(pageReqVO),
                BrokerageRecordBizTypeEnum.ORDER.getType(), BrokerageRecordStatusEnum.SETTLEMENT.getStatus(),
                ArrayUtil.get(pageReqVO.getTimes(), 0), ArrayUtil.get(pageReqVO.getTimes(), 1));
        return new PageResult<>(pageResult.getRecords(), pageResult.getTotal());
    }

    @Override
    public Integer getUserRankByPrice(Long userId, LocalDateTime[] times) {
        // 用户的推广金额
        Integer price = brokerageRecordMapper.selectSummaryPriceByUserIdAndBizTypeAndCreateTimeBetween(userId,
                BrokerageRecordBizTypeEnum.ORDER.getType(), BrokerageRecordStatusEnum.SETTLEMENT.getStatus(),
                ArrayUtil.get(times, 0), ArrayUtil.get(times, 1));
        // 排在用户前面的人数
        Integer greaterCount = brokerageRecordMapper.selectCountByPriceGt(price,
                BrokerageRecordBizTypeEnum.ORDER.getType(), BrokerageRecordStatusEnum.SETTLEMENT.getStatus(),
                ArrayUtil.get(times, 0), ArrayUtil.get(times, 1));
        // 获得排名
        return ObjUtil.defaultIfNull(greaterCount, 0) + 1;
    }

    /**
     * 减少佣金
     * 1.佣金提现。2.驳回时需要退还用户佣金
     *
     * @param userId         会员编号
     * @param bizType        业务类型
     * @param bizId          业务编号
     * @param brokeragePrice 佣金
     * @param title          标题
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public void addBrokerage(Long userId, BrokerageRecordBizTypeEnum bizType, String bizId, Integer brokeragePrice, String title) {
        // 1. 校验佣金余额
        BrokerageUserDO user = brokerageUserService.getBrokerageUser(userId);
        int balance = Optional.of(user)
                .map(BrokerageUserDO::getBrokeragePrice).orElse(0);
        if (balance + brokeragePrice < 0) {
            throw exception(BROKERAGE_WITHDRAW_USER_BALANCE_NOT_ENOUGH, MoneyUtils.fenToYuanStr(balance));
        }

        // 2. 更新佣金余额
        boolean success = brokerageUserService.updateUserPrice(userId, brokeragePrice);
        if (!success) {
            // 失败时，则抛出异常。只会出现扣减佣金时，余额不足的情况
            throw exception(BROKERAGE_WITHDRAW_USER_BALANCE_NOT_ENOUGH, MoneyUtils.fenToYuanStr(balance));
        }

        // 3. 新增记录
        BrokerageRecordDO record = BrokerageRecordConvert.INSTANCE.convert(user, bizType, bizId, 0, brokeragePrice,
                null, title, null, null);
        brokerageRecordMapper.insert(record);
    }

    @Override
    public AppBrokerageProductPriceRespVO calculateProductBrokeragePrice(Long userId, Long spuId) {
        // 1. 构建默认的返回值
        AppBrokerageProductPriceRespVO respVO = new AppBrokerageProductPriceRespVO().setEnabled(false)
                .setBrokerageMinPrice(0).setBrokerageMaxPrice(0);

        // 2.1 校验分销功能是否开启
        TradeConfigDO tradeConfig = tradeConfigService.getTradeConfig();
        if (tradeConfig == null || BooleanUtil.isFalse(tradeConfig.getBrokerageEnabled())) {
            return respVO;
        }
        // 2.2 校验用户是否有分销资格
        respVO.setEnabled(brokerageUserService.getUserBrokerageEnabled(getLoginUserId()));
        if (BooleanUtil.isFalse(respVO.getEnabled())) {
            return respVO;
        }
        // 2.3 校验商品是否存在
        ProductSpuRespDTO spu = productSpuApi.getSpu(spuId);
        if (spu == null) {
            return respVO;
        }

        // 3.1 商品单独分佣模式
        Integer fixedMinPrice = 0;
        Integer fixedMaxPrice = 0;
        Integer spuMinPrice = 0;
        Integer spuMaxPrice = 0;
        List<ProductSkuRespDTO> skuList = productSkuApi.getSkuListBySpuId(ListUtil.of(spuId));
        if (BooleanUtil.isTrue(spu.getSubCommissionType())) {
            fixedMinPrice = getMinValue(skuList, ProductSkuRespDTO::getFirstBrokeragePrice);
            fixedMaxPrice = getMaxValue(skuList, ProductSkuRespDTO::getFirstBrokeragePrice);
            // 3.2 全局分佣模式（根据商品价格比例计算）
        } else {
            spuMinPrice = getMinValue(skuList, ProductSkuRespDTO::getPrice);
            spuMaxPrice = getMaxValue(skuList, ProductSkuRespDTO::getPrice);
        }
        respVO.setBrokerageMinPrice(calculatePrice(spuMinPrice, tradeConfig.getBrokerageFirstPercent(), fixedMinPrice));
        respVO.setBrokerageMaxPrice(calculatePrice(spuMaxPrice, tradeConfig.getBrokerageFirstPercent(), fixedMaxPrice));
        return respVO;
    }

    /**
     * 奖励触发任务 - 检查推广员是否完成“一推二”任务，并发放奖励
     * 核心规则：
     * 1. 首次完成一推二 -> 发放基础奖励（根据 keyword 决定）
     * 2. 后续重复完成 -> 累加奖励：第2~5次每次 +300，第6次重置为基础奖励
     */
    @Override
    public int rewardTriggerJob() {
        // 1. 获取系统分销配置
        TradeConfigDO memberConfig = tradeConfigService.getTradeConfig();
        Integer brokerageFrozenDays = memberConfig.getBrokerageFrozenDays();
        // 1.1 计算佣金解冻时间
        LocalDateTime unfreezeTime = null;
        if (brokerageFrozenDays != null && brokerageFrozenDays > 0) {
            unfreezeTime = LocalDateTime.now().plusDays(brokerageFrozenDays);
        }

        // 2. 获取所有-启用状态的推广员
        List<BrokerageUserDO> brokerageUsers = brokerageUserService.getAllEnabledUsers();
        // 3. 查询包含“和田玉”的商品（例如：戒指、吊坠、手镯、三件套等） 或者 直接查询首饰的商品如果被定死
        List<ProductSpuRespDTO> spuList = productSpuApi.getSpuByLikeName("【限时特惠】和田玉");
        if (CollectionUtil.isEmpty(spuList)) return 0;

        // 3.1 按商品 keyword 数字升序排序，非数字排最后
        spuList.sort(Comparator.comparing(spu -> {
            try {
                return Integer.parseInt(spu.getKeyword());
            } catch (NumberFormatException e) {
                return Integer.MAX_VALUE; // 非数字 keyword 排到最后
            }
        }));
        // 用于批量保存的佣金记录列表
        List<BrokerageRecordDO> records = new ArrayList<>();

        // 4. 遍历每个推广员，判断是否完成“一推二”任务
        for (BrokerageUserDO brokerageUser : brokerageUsers) {
            Long userId = brokerageUser.getId();
            // 4.1 校验用户状态是否仍启用 跳过已禁用的推广员
            if (!BooleanUtil.isTrue(brokerageUser.getBrokerageEnabled())) {
                continue;
            }
            // 4.2 获取一级下线 （直推用户）
            List<BrokerageUserDO> firstLevelUsers = brokerageUserService.getDirectInvitees(userId);
            if (firstLevelUsers.size() < 2) {
                continue; // 一级下线不足2人，不满足“一推二”
            }
            // 获取一级用户ID集合
            List<Long> firstUserIds = firstLevelUsers.stream()
                    .map(BrokerageUserDO::getId)
                    .collect(Collectors.toList());

            // 遍历每个商品，检测推广员任务完成情况
            for (ProductSpuRespDTO spu : spuList) {
                String keyword = spu.getKeyword(); // 拿出对应的编号
                String productName = spu.getName(); // 产品名称
                Integer price = spu.getPrice(); // 产品价格
                if (price == null || price <= 0) {
                    continue;
                }
                // 4.3 检查是否已经领取过该商品任务奖励
                List<BrokerageRecordDO> alreadyRewarded = brokerageRecordMapper.selectList(
                        new LambdaQueryWrapperX<BrokerageRecordDO>()
                                .eq(BrokerageRecordDO::getUserId, userId)
                                .eq(BrokerageRecordDO::getBizId, keyword)
                                .like(BrokerageRecordDO::getTitle, BrokerageRecordBizTypeEnum.INVITE_TASK_REWARD.getTitle())
                );
                // 如果存在奖励，判断是否符合累计金额奖励
                if (alreadyRewarded.size() > 0) {
                    // —— 已经领取过：走累计奖励逻辑 ——
                    int totalGroups = calculateCompletedGroups(firstUserIds, productName);
                    int completedTeams = totalGroups / 2; // 每2组算1个团队
                    //  拿出最大的领取次数
                    int maxRewardedStage = alreadyRewarded.stream()
                            .mapToInt(BrokerageRecordDO::getRewardedCount)  // 这里用 getRewardedCount() 方法获取字段
                            .max()
                            .orElse(0);  // 如果没有，默认0
                    //  这个地方需要判断当前的团队的数量和最大的领取次数，判断是否发放奖励以及补发
                    if (completedTeams <= 1) {
                        // 不满足发放最低奖励要求，跳过
                        log.info("用户 {} 未完成足够团队数，不发放奖励", userId);
                        continue;
                    }
                    if (completedTeams < maxRewardedStage) {
                        // 异常情况：已发放奖励阶段比完成阶段多，记录警告日志
                        log.warn("用户 {} 已领取奖励阶段 {} 超过实际完成阶段 {}", userId, maxRewardedStage, completedTeams);
                        continue;
                    }
                    if (completedTeams == maxRewardedStage) {
                        // 奖励已发放到当前阶段，无需重复发放
                        log.info("用户 {} 已成功完成一推二累计奖励，当前阶段 {} 已发放，无需重复", userId, completedTeams);
                        continue;
                    }
                    // 补发未发放的奖励（漏发或者多阶段一次完成）
                    for (int stage = maxRewardedStage + 1; stage <= completedTeams; stage++) {
                        int brokeragePrice = calculateSubsidy(keyword, stage);
                        records.add(createRewardRecord(brokerageUser, keyword, brokeragePrice, brokerageFrozenDays, unfreezeTime, stage));
                        updateUserBrokerage(userId, brokeragePrice, brokerageFrozenDays);
                        log.info("用户 {} （补发）发放一推二累计奖励，阶段 {}，金额 {}", userId, stage, brokeragePrice);
                    }
                } else {
                    // —— 首次领取逻辑 ——
                    if (isFirstTimeQualified(firstLevelUsers, firstUserIds, productName)) {
                        // 项目一：3648+688，累计168
                        // 项目二：12480+1888，累计500
                        // 项目三：31548+2888，累计700
                        // 项目四：62130+3888，累计1000
                        int brokeragePrice = getExtraRewardByKeyword(keyword);
                        records.add(createRewardRecord(brokerageUser, keyword, brokeragePrice, brokerageFrozenDays, unfreezeTime, 1));
                        updateUserBrokerage(userId, brokeragePrice, brokerageFrozenDays);
                        log.info("用户 {} 首次完成一推二奖励发放：{}", userId, brokeragePrice);
                    }
                }
            }
        }
        // 5. 批量保存奖励记录
        if (!records.isEmpty()) {
            brokerageRecordMapper.insertBatch(records);
        }
        return records.size();
    }

    /**
     * 计算用户完成的“一推二”组数
     */
    private int calculateCompletedGroups(List<Long> firstUserIds, String productName) {
        // 查询一级下线购买次数
        Map<Long, Long> firstBuyCountMap = brokerageRecordMapper.selectList(
                        new LambdaQueryWrapperX<BrokerageRecordDO>()
                                .in(BrokerageRecordDO::getSourceUserId, firstUserIds)
                                .eq(BrokerageRecordDO::getSourceUserLevel, 1)
                                .like(BrokerageRecordDO::getTitle, productName)
                ).stream()
                .collect(Collectors.groupingBy(BrokerageRecordDO::getSourceUserId, Collectors.counting()));

        int totalGroups = 0;
        for (Long firstUserId : firstUserIds) {
            long firstBuyCount = firstBuyCountMap.getOrDefault(firstUserId, 0L);
            if (firstBuyCount == 0) continue;

            // 查询二级下线
            List<Long> secondLevelIds = brokerageUserService.getDirectInvitees(firstUserId).stream()
                    .map(BrokerageUserDO::getId)
                    .collect(Collectors.toList());
            if (secondLevelIds.isEmpty()) continue;

            // 查询二级购买次数
            long secondBuyCount = brokerageRecordMapper.selectCount(
                    new LambdaQueryWrapperX<BrokerageRecordDO>()
                            .in(BrokerageRecordDO::getSourceUserId, secondLevelIds)
                            .eq(BrokerageRecordDO::getSourceUserLevel, 1)
                            .like(BrokerageRecordDO::getTitle, productName)
            );
            // 一级下线 A 买了 2 次
            //A 的下级总共买了 5 次
            //组数 = min(2, 5 / 2) = min(2, 2) = 2 组
            // 每个一级下线贡献的组数 = min(一级购买次数, 二级购买次数 / 2)
            totalGroups += Math.min(firstBuyCount, secondBuyCount / 2);
        }
        return totalGroups;
    }

    /**
     * 检查用户是否首次达成“一推二”条件
     */
    private boolean isFirstTimeQualified(List<BrokerageUserDO> firstLevelUsers, List<Long> firstUserIds, String productName) {
        // 至少2个一级下线购买
        List<Long> distinctFirstBuyerIds = brokerageRecordMapper.selectList(
                        new LambdaQueryWrapperX<BrokerageRecordDO>()
                                .in(BrokerageRecordDO::getSourceUserId, firstUserIds)
                                .eq(BrokerageRecordDO::getSourceUserLevel, 1)
                                .like(BrokerageRecordDO::getTitle, productName)
                ).stream()
                .map(BrokerageRecordDO::getSourceUserId)
                .distinct()
                .collect(Collectors.toList());
        if (distinctFirstBuyerIds.size() < 2) return false;

        // 至少2个一级下线各自都有 >= 2 个二级下线完成购买
        int qualifiedCount = 0;
        for (BrokerageUserDO subUser : firstLevelUsers) {
            List<Long> secondLevelIds = brokerageUserService.getDirectInvitees(subUser.getId()).stream()
                    .map(BrokerageUserDO::getId)
                    .collect(Collectors.toList());
            if (secondLevelIds.size() < 2) continue;

            // 判断下级中是否有至少 2 个不同用户购买了当前商品
            List<Long> distinctSecondBuyerIds = brokerageRecordMapper.selectList(
                            new LambdaQueryWrapperX<BrokerageRecordDO>()
                                    .in(BrokerageRecordDO::getSourceUserId, secondLevelIds)
                                    .eq(BrokerageRecordDO::getSourceUserLevel, 1)
                                    .like(BrokerageRecordDO::getTitle, productName)
                    ).stream()
                    .map(BrokerageRecordDO::getSourceUserId)
                    .distinct()
                    .collect(Collectors.toList());
            if (distinctSecondBuyerIds.size() >= 2) {
                qualifiedCount++;
                if (qualifiedCount >= 2) return true;
            }
        }
        return false;
    }

    /**
     * 创建佣金奖励记录
     */
    private BrokerageRecordDO createRewardRecord(BrokerageUserDO user, String keyword, int amount,
                                                 Integer frozenDays, LocalDateTime unfreezeTime, Integer rewardedCount) {
        BrokerageRecordDO brokerageRecordDO = BrokerageRecordConvert.INSTANCE.convert(
                user, BrokerageRecordBizTypeEnum.INVITE_TASK_REWARD, keyword,
                frozenDays, amount, unfreezeTime,
                BrokerageRecordBizTypeEnum.INVITE_TASK_REWARD.getTitle(), null, null
        );
        brokerageRecordDO.setRewardedCount(rewardedCount);
        return brokerageRecordDO;
    }

    /**
     * 更新用户佣金
     *
     * @param userId     用户id
     * @param amount     冻结金额
     * @param frozenDays 冻结天数
     */
    private void updateUserBrokerage(Long userId, int amount, Integer frozenDays) {
        if (frozenDays != null && frozenDays > 0) { // 更新用户冻结佣金
            brokerageUserService.updateUserFrozenPrice(userId, amount);
        } else { // 更新用户可用佣金
            brokerageUserService.updateUserPrice(userId, amount);
        }
    }

    /**
     * 获取项目的基础补贴金额
     *
     * @param keyword 顺序：1-4
     */
    static int getExtraRewardByKeyword(String keyword) {
        switch (keyword) {
            case "1":
                return 688 * 100;
            case "2":
                return 1888 * 100;
            case "3":
                return 2888 * 100;
            case "4":
                return 3888 * 100;
            default:
                return 0;
        }
    }

    /**
     * 获取每次团队完成后的累加金额
     */
    private static int getIncrementRewardByKeyword(String keyword) {
        switch (keyword) {
            case "1":
                return 168 * 100;
            case "2":
                return 500 * 100;
            case "3":
                return 700 * 100;
            case "4":
                return 1000 * 100;
            default:
                return 0;
        }
    }

    /**
     * 根据当前团队完成次数计算补贴金额
     */
    private static int calculateSubsidy(String keyword, int completedTeams) {
        int baseReward = getExtraRewardByKeyword(keyword); // 初始奖励金额
        int increment = getIncrementRewardByKeyword(keyword); // 递增金额

        // 1~5 次递增，第 6 次重置
        int positionInCycle = ((completedTeams - 1) % 5) + 1;
        if (positionInCycle == 1) {
            return baseReward;
        } else if (positionInCycle >= 2) {
            return baseReward + (positionInCycle - 1) * increment;
        } else { // 第 6 次
            return baseReward;
        }
    }

    /**
     * 获得自身的代理对象，解决 AOP 生效问题
     *
     * @return 自己
     */
    private BrokerageRecordServiceImpl getSelf() {
        return SpringUtil.getBean(getClass());
    }

}
